package com.magima.core.connect;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.http.HttpStatus;

import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.magima.core.app.BaseActivity;
import com.magima.core.app.BaseApplication;
import com.magima.core.app.ExceptionConstants;
import com.magima.core.app.IExceptionCallback;
import com.magima.core.app.ProfilerConstants;
import com.magima.core.connect.ServerOperator.ServerResult;
import com.magima.core.connect.listener.ServerTaskListener;
import com.magima.core.ui.LoadingUIProvider;

public class ServerAsyncTask extends Thread{
	
	protected Context mContext;
	private String mUrl = null;
	private String mMethod = null;

	private HashMap<String, String> mParams = null;
	private String mParamsStr = null;
	private boolean mJsonString = false;

	private boolean mDiscussDefaultException = false;
	private boolean mNeedDialog = true;
	private ServerTaskListener mListener = null;
	private int mSocketTimeout = 0;
	private InternalHandler mHandler;
	private OnCancelListener mOnCancelListener;
	
	private static LoadingUIProvider globalLoadingUIProvider;
	public static void setGlobalLoadingUIProvider(LoadingUIProvider onLoadingUIChanged){
		globalLoadingUIProvider = onLoadingUIChanged;
	}
	
	private static HashMap<String, String> globalHeader;
	public static void setGlobalHeader(HashMap<String, String> header){
		globalHeader = header;
	}
	
	public static HashMap<String, String> getGlobalHeader(){
		return globalHeader;
	}

	private static HashMap<String, List<ServerTaskListener>> getTaskQueuePool = new HashMap<String, List<ServerTaskListener>>();
	
	private class MyOnCancelListener implements OnCancelListener {
		private OnCancelListener mOnCancelListener;
		private WeakReference<ServerAsyncTask> mTask;
		public MyOnCancelListener(OnCancelListener listener, ServerAsyncTask task){
			mOnCancelListener = listener;
			mTask = new WeakReference<ServerAsyncTask>(task);
		}
		@Override
		public void onCancel(DialogInterface arg0) {
			if ( mTask.get() != null ) {
				if ( mTask.get().isAlive() )
					mTask.get().interrupt();				
			}				
			if ( mOnCancelListener != null )
				mOnCancelListener.onCancel(arg0);
		}
	}

	/**
	 * 
	 * @param context 						上下文
	 * @param url							请求url
	 * @param method						请求服务器的方法，设定Get请求或者是Post请求
	 * @param params						可以为null， HashMap<String, String> params参数
	 * @param paramsStr					可以为null，  String paramsStr参数
	 * @param needDialog					boolean值，默认true
	 * @param jsonString					boolean值，默认false
	 * @param discussDefaultException		boolean值，默认false
	 * @param listener						ServerTaskListener接口对象，new一个新的进去重写里面的方法，监听请求失败还是成功
	 * @param socketTimeout				timeout时间
	 * @param onCancelListener				取消的回调
	 */
	private ServerAsyncTask(Context context, String url, String method, HashMap<String, String> params, 
			String paramsStr, boolean needDialog, boolean jsonString, boolean discussDefaultException, ServerTaskListener listener,
			int socketTimeout, OnCancelListener onCancelListener) {
		super();
		this.mContext = context;
		this.mUrl = url;
		this.mMethod = method;
		this.mParams = params;
		this.mParamsStr = paramsStr;
		this.mNeedDialog = needDialog;
		this.mJsonString = jsonString;
		this.mDiscussDefaultException = discussDefaultException;
		this.mListener = listener;
		this.mSocketTimeout = socketTimeout;
		this.mOnCancelListener = onCancelListener;
		if ( Looper.myLooper() != null ) //新建本对象实例的上下文在UI线程
			this.mHandler = new InternalHandler(mContext, mListener, this);
	}
	
	public ServerAsyncTask(Context context, String url, String method, HashMap<String, String> params, 
			boolean needDialog, boolean jsonString, boolean discussDefaultException, ServerTaskListener listener) {
		this(context, url, method, params, null, needDialog, jsonString, discussDefaultException, listener, 0, null);
	}
	
	public ServerAsyncTask(Context context, String url, String method, HashMap<String, String> params, 
			boolean needDialog, boolean jsonString, ServerTaskListener listener, int socketTimeout, OnCancelListener onCancelListener) {
		this(context, url, method, params, null, needDialog, jsonString, false, listener, socketTimeout, onCancelListener);
	}
	
	public ServerAsyncTask(Context context, String url, String method, HashMap<String, String> params, 
			boolean needDialog, boolean jsonString, ServerTaskListener listener, int socketTimeout) {
		this(context, url, method, params, null, needDialog, jsonString, false, listener, socketTimeout, null);		
	}
	
	public ServerAsyncTask(Context context, String url, String method, HashMap<String, String> params, 
			boolean needDialog, boolean jsonString, ServerTaskListener listener) {
		this(context, url, method, params, null, needDialog, jsonString, false, listener, 0, null);		
	}

	public ServerAsyncTask(Context context, String url, String method, String params, boolean needDialog, ServerTaskListener listener) {
		this(context, url, method, null, params, needDialog, false, false, listener, 0, null);		
	}

	public ServerAsyncTask(Context context, String url, String method, HashMap<String, String> params, ServerTaskListener listener) {
		this(context, url, method, params, null, true, false, false, listener, 0, null);		
	}
	
	public ServerAsyncTask(Context context, String url, String method, HashMap<String, String> params, ServerTaskListener listener, OnCancelListener onCancelListener) {
		this(context, url, method, params, null, true, false, false, listener, 0, onCancelListener);		
	}
	
	public String getMethod(){
		return mMethod;
	}
	
	public String getUrl(){
		return mUrl;
	}
	
	public boolean getDiscussDefaultException(){
		return mDiscussDefaultException;
	}
	
	public void execute(){
		BaseApplication app = BaseApplication.fromContext(mContext);
		if(checkNetAvailable(mContext)){
			if ( mMethod.equalsIgnoreCase("GET") ) {
				synchronized(getTaskQueuePool){
					List<ServerTaskListener> queue = getTaskQueuePool.get(mUrl);
					if ( queue == null ) {// if has not start yet
						queue = new ArrayList<ServerTaskListener>();
						getTaskQueuePool.put(mUrl, queue);
						queue.add(mListener);
					} else { // if already start
						Log.i("getData-------------","fetching "+mUrl+" is already start.");
						queue.add(mListener);
						return;
					}
				}
			}
			showProgressDialog();
			if(mContext instanceof BaseActivity)
				((BaseActivity)(mContext)).addTask(this);
			if(app != null){
				app.getProfilerTool().setOutPara(ProfilerConstants.THREAD_COUNT, Thread.getAllStackTraces().keySet().size(), false);
			}
			super.start();
			if(app != null){
				app.getProfilerTool().setOutPara(ProfilerConstants.THREAD_COUNT, Thread.getAllStackTraces().keySet().size(), false);
			}
		}else{
			if (globalLoadingUIProvider != null ){
				globalLoadingUIProvider.clearLoadingUI(mContext);
			}
			
			if(mListener != null) {
				mListener.onError(ExceptionConstants.CODE_NET_UNAVAILABLE);
				if(app != null){
					app.getProfilerTool().setOutPara(ProfilerConstants.THREAD_COUNT, Thread.getAllStackTraces().keySet().size(), false);
				}
			}
		}
	}
	
	@Override
	public void start(){
		this.execute();
	}
	
	private static boolean checkNetAvailable(Context context){
		if ( context == null )
			return false;
		ConnectivityManager connMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
		
		NetworkInfo wifiInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
		NetworkInfo mobileInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
		
		if((wifiInfo!= null && wifiInfo.isConnected()) || (mobileInfo != null && mobileInfo.isConnected())){
			return true;
		}else{
			return false;
		}
	}

	@Override
	public void run() {
		ServerResult result = ServerOperator.httpsConnect(mContext, mUrl, mMethod, globalHeader, mParams, mParamsStr, mJsonString, mSocketTimeout);
		Message msg = new Message();
		msg.obj = result;
		if ( mHandler != null ) { //新建本对象实例的上下文在UI线程
			mHandler.sendMessage(msg);
		} else { //不在主线程
			InternalHandler.handleResultMessage(mContext, msg, mListener, this);
		}
	}
	
	private void showProgressDialog(){
		if(mNeedDialog && globalLoadingUIProvider != null){
			globalLoadingUIProvider.startLoadingUI(mContext, mOnCancelListener == null ?
					null : new MyOnCancelListener(mOnCancelListener, this));
		}
	}
	
	public boolean getNeedDialog(){
		return mNeedDialog;
	}
	
	private static class InternalHandler extends Handler {
		private ServerTaskListener listener = null;
		private Context mContext;
		private ServerAsyncTask task;

		public InternalHandler(Context c, ServerTaskListener l, ServerAsyncTask task){
			this.mContext = c;
			this.listener = l;
			this.task = task;
		}

		@Override  
		public void handleMessage(Message msg) {
			handleResultMessage(mContext, msg, listener, task);
		}
		
		public static void handleInternalServerError(Context context, String r, boolean discussDefaultException){
			BaseApplication app = BaseApplication.fromContext(context);
			Gson gson = new GsonBuilder().create();
			try {
				ServerErrorCode sec = gson.fromJson(r, ServerErrorCode.class);
				
				IExceptionCallback callback = app.getExceptionManager().findByTypeWithoutDefault(ExceptionConstants.CODE_SC_INTERNAL_SERVER_ERROR, sec.code);
				if(callback == null && !discussDefaultException){
					callback = app.getExceptionManager().getDefaultType();
				}
				if(callback != null){
					callback.onEvent(context, ExceptionConstants.CODE_SC_INTERNAL_SERVER_ERROR, sec.code);
				}
			} catch (Exception e){
				IExceptionCallback callback = app.getExceptionManager().findByTypeWithoutDefault(ExceptionConstants.CODE_SC_INTERNAL_SERVER_ERROR, 0);
				if(callback == null && !discussDefaultException){
					callback = app.getExceptionManager().getDefaultType();
				}
				if(callback != null ){
					callback.onEvent(context, ExceptionConstants.CODE_SC_INTERNAL_SERVER_ERROR, 0);
				}
				e.printStackTrace();
			}
		}
		
		private static void mantainTask(Context context, ServerAsyncTask task){
			if(context instanceof BaseActivity)
				((BaseActivity)(context)).removeTask(task);
			
			if ( task.getNeedDialog() && globalLoadingUIProvider != null ){
				globalLoadingUIProvider.stopLoadingUI(context);
			}
		}
		
		public static void handleResultMessage(Context context, Message msg, ServerTaskListener listener, ServerAsyncTask task){
			mantainTask(context, task);
			
			if ( task.getMethod().equalsIgnoreCase("GET") ) {
				List<ServerTaskListener> queue;
				synchronized ( getTaskQueuePool ) {
					queue = getTaskQueuePool.get(task.getUrl());
					getTaskQueuePool.remove(task.getUrl());
				}
				if ( queue != null ) {
					for ( ServerTaskListener l : queue ) {
						handleResultMessage(context, msg, task.getDiscussDefaultException(), l);
					}
				} else {
					//its an error here
					Exception e = new Exception();
					e.printStackTrace();
					handleResultMessage(context, msg, task.getDiscussDefaultException(), listener);
				}				
			} else {				
				handleResultMessage(context, msg, task.getDiscussDefaultException(), listener);
			}
		}
		
		public static void handleResultMessage(Context context, Message msg, boolean discussDefaultException, ServerTaskListener listener){
			BaseApplication app = BaseApplication.fromContext(context);
			
			if(msg.obj == null && listener != null)
				listener.onError(ExceptionConstants.CODE_EXCEPTION);
			
			ServerResult sr = (ServerResult)msg.obj;
			int type = sr.code;
			String r = sr.result;
			
			switch (type) {
			case ExceptionConstants.CODE_SC_OK:
				if(listener != null)
					listener.onSuccess(r);
				break;
				
			case ExceptionConstants.CODE_SC_INTERNAL_SERVER_ERROR:
				handleInternalServerError(context, r, discussDefaultException);
				if(listener != null)
					listener.onError(HttpStatus.SC_INTERNAL_SERVER_ERROR);
				break;
				
			case ExceptionConstants.CODE_SC_UNAUTHORIZED:
			case ExceptionConstants.CODE_SC_FORBIDDEN:
			case ExceptionConstants.CODE_TIMEOUT_EXCEPTION:
			case ExceptionConstants.CODE_SSL_PEER_UNVERIFIED_EXCEPTION:
			case ExceptionConstants.CODE_EXCEPTION:
			case ExceptionConstants.CODE_HOST_UNKNOWN_EXCEPTION:
			case ExceptionConstants.CODE_URL_MALFORMED_EXCEPTION:
			case ExceptionConstants.CODE_CLIENT_PROTOCOL_EXCEPTION:
            case ExceptionConstants.CODE_MISSING_INTERNET_PERMISSION:
			default:
				IExceptionCallback callback = app.getExceptionManager().findByTypeWithoutDefault(type);
				if(callback == null && !discussDefaultException){
					callback = app.getExceptionManager().getDefaultType();
				}
				if(callback != null){
					callback.onEvent(context, type, 0);
				}
				if(listener != null)
					listener.onError(type);
				break;
			}
			
			
		}
	}
}
